package com.xiaomaoguai.fcp.kepler.utlis.json;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.experimental.UtilityClass;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.text.SimpleDateFormat;

/**
 * @author WeiHui-Z
 * @version v1.0.0
 * @date 2019/10/10 9:54
 * @since JDK 1.8
 */
@UtilityClass
public final class JsonUtils {

	/**
	 * ObjectMapper
	 */
	private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

	static {
		OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
		OBJECT_MAPPER.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
		OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
		OBJECT_MAPPER.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);
		// 允许出现特殊字符和转义符
		OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
		// 允许出现单引号
		OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
		OBJECT_MAPPER.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
	}

	/**
	 * 转换成JSON格式的字符串
	 *
	 * @param object 对象
	 * @return Json 字符串
	 */
	public static String toJsonString(Object object) {
		try {
			return (object instanceof String) ? (String) object : OBJECT_MAPPER.writeValueAsString(object);
		} catch (JsonProcessingException e) {
			throw new IllegalArgumentException(e);
		}
	}

	/**
	 * 转换成JSON格式的字符串-并格式化
	 *
	 * @param object 对象
	 * @return Json 字符串
	 */
	public static String toFormatJsonString(Object object) {
		try {
			return (object instanceof String) ? formatJson((String) object) : OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(object);
		} catch (JsonProcessingException e) {
			throw new IllegalArgumentException(e);
		}
	}

	/**
	 * 格式化
	 *
	 * @param jsonStr json字符串
	 * @return String 格式化的json字符串
	 */
	public static String formatJson(String jsonStr) {
		if (StringUtils.isBlank(jsonStr)) {
			return null;
		}
		StringBuilder sb = new StringBuilder();
		char last = '\0';
		char current = '\0';
		int indent = 0;
		for (int i = 0; i < jsonStr.length(); i++) {
			last = current;
			current = jsonStr.charAt(i);
			switch (current) {
				case '{':
				case '[':
					sb.append(current);
					sb.append('\n');
					indent++;
					addIndentBlank(sb, indent);
					break;
				case '}':
				case ']':
					sb.append('\n');
					indent--;
					addIndentBlank(sb, indent);
					sb.append(current);
					break;
				case ',':
					sb.append(current);
					if (last != '\\') {
						sb.append('\n');
						addIndentBlank(sb, indent);
					}
					break;
				default:
					sb.append(current);
			}
		}

		return sb.toString();
	}

	/**
	 * 添加space
	 *
	 * @param sb
	 * @param indent
	 */
	private static void addIndentBlank(StringBuilder sb, int indent) {
		for (int i = 0; i < indent; i++) {
			sb.append('\t');
		}
	}

	/**
	 * 将 Json string转化为 ObjectNode
	 *
	 * @param json json串
	 * @return ObjectNode
	 * @throws IOException ex
	 */
	public static ObjectNode readTree(String json) throws IOException {
		return StringUtils.isBlank(json) ? null : (ObjectNode) getObjectMapper().readTree(json);
	}

	/**
	 * 把JSON格式的字符串解析成普通对象。比如：
	 * * <code>
	 * * UserDto dto = BaseDto.fromJson(str, UserDto.class);
	 * * </code>
	 *
	 * @param json  字符串
	 * @param clazz 对象
	 * @param <T>   对象类型
	 * @return 对象实例
	 */
	public static <T> T toJavaObject(String json, Class<T> clazz) {
		try {
			return OBJECT_MAPPER.readValue(json, clazz);
		} catch (IOException e) {
			throw new IllegalArgumentException(e);
		}
	}

	/**
	 * 把JSON格式的字符串解析成泛型类。比如：
	 * <code>
	 * Map&lt;String, Object&gt; map = BaseDto.fromJson(str, new TypeReference&lt;Map&lt;String, Object&gt;&gt;() {});
	 * </code>
	 *
	 * @param json          字符串
	 * @param typeReference 对象
	 * @param <T>           对象类型
	 * @return 对象
	 */
	public static <T> T toJavaObject(String json, TypeReference<T> typeReference) {
		try {
			return OBJECT_MAPPER.readValue(json, typeReference);
		} catch (IOException e) {
			throw new IllegalArgumentException(e);
		}
	}

	public static ObjectMapper getObjectMapper() {
		return OBJECT_MAPPER;
	}

}
